package simplescenegraph;

import java.util.List;

import simplescenegraph.math.Vector3f;

public class BoundingSphere extends BoundingVolume {
	
	private Vector3f center;
	
	private float radius;
	
	public BoundingSphere() {
		radius = 1;
		center = new Vector3f();
	}
	
	public BoundingSphere(float radius, Vector3f center) {
		if (radius <= 0 || center == null) {
			throw new IllegalArgumentException();
		}
		
		this.radius = radius;
		this.center = new Vector3f(center);
	}
	
	public Vector3f getCenter() {
		Vector3f c0 = new Vector3f(center);
		localTransform.transform(c0);
		return c0;
	}
	
	public float getRadius() {
		return radius * localTransform.getHighestScaleFactor();
	}
	
	@Override
	public void merge(BoundingVolume otherBoundingVolume) {
		if (otherBoundingVolume == null) {
			throw new IllegalArgumentException();
		}
		
		if (!(otherBoundingVolume instanceof BoundingSphere)) {
			throw new IllegalArgumentException("Cannot merge a bounding sphere with this a bounding volume of this type.");
		}
		
		BoundingSphere otherBoundingSphere = (BoundingSphere) otherBoundingVolume;
		
		Vector3f c0 = getCenter(), c1 = otherBoundingSphere.getCenter();
		float r0 = getRadius(), r1 = otherBoundingSphere.getRadius();
		
		Vector3f cd = new Vector3f(c0, c1);
		float rd = r1 - r0, rds = rd * rd, lsqr = cd.squaredLength();
		
		if (rds >= lsqr) {
			if (rd > 0) {
				center = c1;
				radius = r1;
			}
			else {
				center = c0;
				radius = r0;
			}
		}
		else {
			float l = (float) Math.sqrt(lsqr), t = (l + r1 - r0) / (2.0f * l);
			
			cd.multiply(t);
			c0.add(cd);
			center = c0;
			radius = (l + r1 + r0) / 2.0f;
		}
		
		localTransform.reset();
	}
	
	@Override
	public void centerAtAverage(List<Vector3f> points) {
		if (points == null || points.isEmpty()) {
			throw new IllegalArgumentException();
		}
		
		center = new Vector3f(points.get(0));
		for (Vector3f point : points) {
			center.add(point);
		}
		
		center.divide(points.size());
		
		for (Vector3f point : points) {
			Vector3f distanceToAnotherPoint = new Vector3f(point, center);
			float greaterRadius = distanceToAnotherPoint.squaredLength();
			if (greaterRadius > radius) {
				radius = greaterRadius;
			}
		}
		radius = (float) Math.sqrt(radius);
		localTransform.reset();
	}
	
	@Override
	public BoundingVolume copy() {
		return new BoundingSphere(getRadius(), getCenter());
	}
	
}
